package helpers

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"sort"
	"testing"
	"time"

	"v8s-api/internal/configuration"
	"v8s-api/internal/db"
	"v8s/logger"

	"github.com/labstack/gommon/log"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"go.mongodb.org/mongo-driver/bson"

	"go.uber.org/zap"
)

type MongoHelper struct {
	log *zap.SugaredLogger

	client     *db.MongoClient
	properties configuration.MongoProperties
}

func CreateMongoHelper(client *db.MongoClient, properties configuration.MongoProperties) *MongoHelper {
	return &MongoHelper{
		log: logger.Logger().Named("mongo-helper").Sugar(),

		client:     client,
		properties: properties,
	}
}
func (h *MongoHelper) GetDbProjects() db.ProjectsRepository {
	return *db.CreateProjectsRepository(h.client, h.properties)
}
func (h *MongoHelper) InsertProjects(file string) {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		panic(fmt.Sprintf("cannot open file '%s': %s", file, err))
	}

	var projects []db.Project
	err = json.Unmarshal(data, &projects)
	if err != nil {
		panic(fmt.Sprintf("failed to unmarshal file '%s': %s", file, err))
	}

	documents := make([]interface{}, 0, len(projects))
	for _, project := range projects {
		current := bson.M{
			"project-id": project.ID,
			"tasks":      project.Tasks,
		}
		documents = append(documents, current)
	}

	inserted, err := h.client.Database(h.properties.Database).Collection(db.ProjectsCollection).InsertMany(context.Background(), documents)
	if err != nil {
		panic(fmt.Sprintf("failed to insert documents from '%s': %s", file, err))
	}

	log.Infof("inserted: '%s'", inserted.InsertedIDs)
}

func (h *MongoHelper) Clear(ctx context.Context) {
	err := h.client.Database(h.properties.Database).Drop(ctx)
	if err != nil {
		panic(fmt.Errorf("failed to drop database: %s", err))
	}
}

func (h *MongoHelper) AssertProjects(t *testing.T, file string) {
	expected, err := ioutil.ReadFile(file)
	if err != nil {
		panic(fmt.Errorf("failed to open file '%s': %w", file, err))
	}

	cursor, err := h.client.Database(h.properties.Database).Collection(db.ProjectsCollection).Find(context.Background(), bson.M{})
	if err != nil {
		panic(fmt.Errorf("failed to list documents from mongodb: %w", err))
	}

	documents := make([]*db.Project, 0)
	err = cursor.All(context.Background(), &documents)
	if err != nil {
		panic(fmt.Errorf("failed to read from cursor: %w", err))
	}

	mockedDate := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
	log.Info("projects")
	for _, document := range documents {
		log.Info("project")
		for index, task := range document.Tasks {
			log.Info("task")
			if task.Status == db.TaskStatusCreationRequest {
				log.Info("new-task")
				document.Tasks[index].ID = "new-task"
				document.Tasks[index].CreatedAt = mockedDate
				document.Tasks[index].ModifiedAt = mockedDate
				document.Tasks[index].Clusters = nil
			}
			if task.ModifiedAt.Year() != 2000 {
				document.Tasks[index].ModifiedAt = mockedDate
			}

		}

	}
	actual, err := json.MarshalIndent(documents, "", "  ")
	if err != nil {
		panic(fmt.Errorf("failed to marshall documents: %w", err))
	}
	require.JSONEq(t, string(expected), string(actual))
}

func (h *MongoHelper) AssertTasks(t *testing.T, tasks []db.Task, file string) {
	data, err := ioutil.ReadFile(file)
	if err != nil {
		panic(fmt.Errorf("failed to open file '%s': %w", file, err))
	}
	taskwithnodeleted := make([]db.Task, 0)
	for _, task := range tasks {
		if task.Status != db.TaskStatusDeleted {
			taskwithnodeleted = append(taskwithnodeleted, task)
		}
	}

	sort.SliceStable(taskwithnodeleted, func(p, q int) bool {
		return taskwithnodeleted[p].ID < taskwithnodeleted[q].ID
	})

	actual, err := json.MarshalIndent(taskwithnodeleted, "", "  ")
	if err != nil {
		panic(fmt.Errorf("failed to marshall documents: %w", err))
	}

	require.JSONEq(t, string(data), string(actual))
}

func (h *MongoHelper) WriteProjects(t *testing.T, file string) {
	expected, err := ioutil.ReadFile(file)
	if err != nil {
		panic(fmt.Errorf("failed to open file '%s': %w", file, err))
	}

	cursor, err := h.client.Database(h.properties.Database).Collection(db.ProjectsCollection).Find(context.Background(), bson.M{})
	if err != nil {
		panic(fmt.Errorf("failed to list documents from mongodb: %w", err))
	}

	documents := make([]*db.Project, 0)
	err = cursor.All(context.Background(), &documents)
	if err != nil {
		panic(fmt.Errorf("failed to read from cursor: %w", err))
	}
	actual, err := json.MarshalIndent(documents, "", "  ")
	if err != nil {
		panic(fmt.Errorf("failed to marshall documents: %w", err))
	}

	_ = ioutil.WriteFile("test.json", actual, 0644)

	assert.Equal(t, string(expected), string(actual))
}
